package com.service.business.member.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.service.business.base.dto.LoginUserSession;
import com.service.business.base.enums.SmsTypeEnum;
import com.service.business.base.service.CoinService;
import com.service.business.base.service.CoinsPriceService;
import com.service.business.base.service.ConfigService;
import com.service.business.member.controller.rep.MemberStorePageRep;
import com.service.business.member.controller.req.MemberWithdrawReq;
import com.service.business.member.controller.req.TransferAccountReq;
import com.service.business.member.dto.MemberStoreChange;
import com.service.business.member.enums.MemberEnums;
import com.service.business.member.service.MemberGradeConfigService;
import com.service.business.member.service.MemberRWService;
import com.service.business.member.service.MemberService;
import com.service.business.member.service.MemberStoreService;
import com.service.business.order.service.OrderProductHrComboDetailsService;
import com.service.business.support.MemberStoreLockUtils;
import com.service.business.support.SessionUtil;
import com.service.business.support.VerifyCodeUtil;
import com.service.business.utils.MemberAccountUtil;
import com.service.core.exception.CommonException;
import com.service.dao.constants.ConfigConstants;
import com.service.dao.enums.CoinsEnum;
import com.service.dao.enums.CommonStatusEnums;
import com.service.dao.enums.MemberJAType;
import com.service.dao.enums.RwRecordEnum;
import com.service.dao.mapper.JournalAccountMapper;
import com.service.dao.mapper.MemberStoreMapper;
import com.service.dao.mapper.MemberWalletMapper;
import com.service.dao.mapper.RwRecordMapper;
import com.service.dao.model.*;
import com.service.dao.po.MemberStorePO;
import com.service.utility.common.TotpUtil;
import com.service.utility.common.UUIDUtil;
import com.service.utility.modelmapper.ModelMapperUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <dl>
 * <dt>back-end</dt>
 * <dd>Description:</dd>
 * <dd>Copyright: Copyright (C) 2015</dd>
 * <dd>Company: </dd>
 * <dd>CreateDate: 2019年09月24日</dd>
 * </dl>
 *
 * @author
 */
@Slf4j
@Service
@Transactional(readOnly = true, rollbackFor = Exception.class)
public class MemberStoreServiceImpl extends ServiceImpl<MemberStoreMapper, MemberStore> implements MemberStoreService {

    @Autowired
    private MemberStoreLockUtils storeLockUtils;

    @Autowired
    private SessionUtil sessionUtil;
    @Autowired
    private VerifyCodeUtil verifyCodeUtil;
    @Autowired
    private ConfigService configService;
    @Autowired
    private MemberRWService memberRWService;
    @Autowired
    private MemberGradeConfigService gradeConfigService;
    @Autowired
    private CoinService coinService;
    @Autowired
    private MemberService memberService;
    @Autowired
    private RwRecordMapper rwRecordMapper;
    @Autowired
    private MemberStoreMapper storeMapper;
    @Autowired
    private MemberWalletMapper walletMapper;
    @Autowired
    private JournalAccountMapper journalAccountMapper;
    @Autowired
    private CoinsPriceService coinsPriceService;
    @Autowired
    private OrderProductHrComboDetailsService orderProductHrComboDetailsService;

    @Value("${rechargePrefix}")
    private String rechargePrefix;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void doWithdraw(MemberWithdrawReq withdrawReq) {
        memberService.validSafetyPwd(withdrawReq.getMemberId(), withdrawReq.getSafetyPwd());
        if (withdrawReq.getNum().compareTo(new BigDecimal(0)) <= 0) {
            log.error("会员<{}>提币失败:提币数量小于0", withdrawReq.getMemberId());
            throw new CommonException("提币数量不能为0", "withdraw_fail_by_arpu_zero");
        }

        MemberWallet wallet = walletMapper.selectById(withdrawReq.getWalletId());
        if (ObjectUtils.isEmpty(wallet)) {
            log.error("会员<{}>提币数量:{},钱包id:{},提币失败:不存在", withdrawReq.getMemberId(), withdrawReq.getNum(), withdrawReq.getWalletId());
            throw new CommonException("提币钱包不存在", "withdraw_fail_by_error_id");
        }
//        withdrawReq.setCoin(CoinsEnum.valueOf(wallet.getCoinsId()));

        Member member = memberService.getById(withdrawReq.getMemberId());

        if (!StringUtils.isEmpty(member.getTotpSecret())) {
            //需要输入谷歌验证码
            if (withdrawReq.getFaCode() == null || withdrawReq.getFaCode() < 100000L ||
                    !TotpUtil.checkCode(member.getTotpSecret(), withdrawReq.getFaCode())) {
                //输入的验证码是空的或者是错误的
                log.error("会员<{}>提币,谷歌验证码错误", withdrawReq.getMemberId());
                throw new CommonException("提币谷歌验证码错误", "two_fa_is_invalid");
            }
        }

        BigDecimal todayTotalWithdrawNum = memberRWService.getSomedayWithdrawNum(withdrawReq.getMemberId(), withdrawReq.getCoin(), new Date());
        gradeConfigService.isValidWithdraw(member, withdrawReq.getCoin(), todayTotalWithdrawNum);

        MemberStorePO store = storeMapper.selectByMemberIdAndCoinId(withdrawReq.getMemberId(), withdrawReq.getCoin().getCode(), CommonStatusEnums.ACTIVE.getCode());
        BigDecimal total = withdrawReq.getNum().add(store.getCoinFee());
        BigDecimal balanceNum = store.getNum();
        if (balanceNum.compareTo(total) < 0) {
            log.error("会员<{}>提币数量:{},钱包id:{},提币失败:会员余额不足", withdrawReq.getMemberId(), withdrawReq.getNum(), withdrawReq.getWalletId());
            throw new CommonException("会员余额不足", "withdraw_fail_by_not_enough_num");
        }

        MemberStoreChange memberStoreChange = MemberStoreChange.builder()
                .memberId(withdrawReq.getMemberId())
                .changeNum(total)
                .coinsId(withdrawReq.getCoin().getCode())
                .coinsName(withdrawReq.getCoin().getName())
                .jaType(MemberJAType.FROST_WITHDRAW)
                .remark("提币操作,数量:" + withdrawReq.getNum() + ",手续费:" + store.getCoinFee())
                .build();
        Integer jaId = changeMemberStore(memberStoreChange);

        //创建会员充提流水
        RwRecord rwRecord = new RwRecord();
        rwRecord.setJaId(jaId);
        rwRecord.setRwNo(chargeOrderNo());
        rwRecord.setMemberId(withdrawReq.getMemberId());
        rwRecord.setRwNum(withdrawReq.getNum());
        rwRecord.setAddress(wallet.getWalletAddr());
        rwRecord.setAddrType(wallet.getWalletType());
        rwRecord.setCoinsId(withdrawReq.getCoin().getCode());
        rwRecord.setCoinsName(withdrawReq.getCoin().getName());
        rwRecord.setPayStatus(RwRecordEnum.PayStatus.submit.getCode());
        rwRecord.setOptType(RwRecordEnum.OptType.withdraw.getCode());
        rwRecord.setUsdtFee(store.getCoinFee());
        rwRecord.setCreateTime(new Date());
        rwRecordMapper.insert(rwRecord);
        log.debug("会员<{}>提币数量:{},手续费:{}, 提币地址:{},提币成功", withdrawReq.getMemberId(), withdrawReq.getNum(), store.getCoinFee(), wallet.getWalletAddr());

    }

    private String chargeOrderNo() {
        return rechargePrefix + UUIDUtil.randomUUID10().substring(0, 8);
    }

    @Override
    public List<MemberStorePageRep> listMemberStore() {
        List<MemberStore> storeList = storeMapper.selectByMemberId(sessionUtil.getUser().getUserId(), CommonStatusEnums.ACTIVE.getCode());
        Map<String, BigDecimal> coinPriceMap = coinsPriceService.getStandardPrice2Map(Boolean.TRUE);
        return storeList.stream().map(item -> {
            MemberStorePageRep pageRep = ModelMapperUtil.getStrictModelMapper().map(item, MemberStorePageRep.class);
            if (StringUtils.equals(pageRep.getCoinsName(), CoinsEnum.POINTS.getName())) {
                pageRep.setTotalConvert(pageRep.getNum());
                return pageRep;
            }
            if(StringUtils.equals(pageRep.getCoinsName(), CoinsEnum.FIL.getName())){
                BigDecimal totalPledgeNum = orderProductHrComboDetailsService.totalPledgeNumByMemberIdAndCoins(item.getMemberId(), item.getCoinsName());
                pageRep.setPledgeNum(totalPledgeNum);
            }
            if (coinPriceMap.containsKey(pageRep.getCoinsName())) {
                pageRep.setTotalConvert(pageRep.getNum().multiply(coinPriceMap.get(pageRep.getCoinsName())));
            } else {
                pageRep.setTotalConvert(pageRep.getNum());
            }
            return pageRep;
        }).collect(Collectors.toList());
    }

    @Override
    public List<MemberStorePO> listMemberStore(Integer userId) {
        List<MemberStore> storeList = storeMapper.selectByMemberId(userId, CommonStatusEnums.ACTIVE.getCode());
        if (ObjectUtils.isEmpty(storeList)) {
            return Lists.newArrayList();
        }
        List<MemberStorePO> result = Lists.newArrayList();
        storeList.forEach(x -> result.add(ModelMapperUtil.getStrictModelMapper().map(x, MemberStorePO.class)));
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void transferAccount(TransferAccountReq accountReq, LoginUserSession user) {
        Integer memberId = user.getUserId();
        if (StringUtils.isNotBlank(accountReq.getVerifyCode())) {
            verifyCodeUtil.verifyTextVerifyCode(user.getAccount(), accountReq.getVerifyCode(), SmsTypeEnum.TURNOUT);
        }
        Integer transfer = coinService.isTransfer(accountReq.getCoin().getCode());
        if (transfer == 0) {
            log.error("该币种暂时无法转账！");
            throw new CommonException("暂时无法转账", "transfer_disabled");
        }
        memberService.validSafetyPwd(memberId, accountReq.getPassword());
        Member loginMember = memberService.getById(memberId);

        Member targetMember = memberService.getMemberByAccount(accountReq.getUserAccount(), MemberEnums.Status.ACTIVE);
        if (ObjectUtils.isEmpty(targetMember)) {
            log.error("会员<{}>转账数量:{},转出账户:{},操作失败，不存在此会员", memberId, accountReq.getTransferNum(), accountReq.getUserAccount());
            throw new CommonException("暂无此会员", "not_found_user");
        }

        if (!isDirectRelativeTransferAccounts(loginMember, targetMember)) {
            log.error("会员<{}>转账数量:{},转出账户:{},操作失败，非直系关系不能转入转出", memberId, accountReq.getTransferNum(), accountReq.getUserAccount());
            throw new CommonException("非直系关系不能转入转出", "member_not_direct_relative");
        }

        if (accountReq.getTransferNum().compareTo(BigDecimal.ZERO) <= 0) {
            log.error("会员<{}>转账数量:{},转出账户:{},操作失败，转出数量不能小于0", memberId, accountReq.getTransferNum(), accountReq.getUserAccount());
            throw new CommonException("转出数量不能小于0", "turn_out_fail_by_number_zero");
        }

        MemberStorePO sourceStore = storeMapper.selectByMemberIdAndCoinId(memberId, accountReq.getCoin().getCode(), CommonStatusEnums.ACTIVE.getCode());
        if (sourceStore.getNum().compareTo(accountReq.getTransferNum()) < 0) {
            log.error("会员<{}>转账数量:{},转出账户:{},操作失败，账户{}余额不足", memberId, accountReq.getTransferNum(), accountReq.getUserAccount(), sourceStore.getCoinsName());
            throw new CommonException("账户余额不足", "store_by_error");
        }

        MemberStoreChange sourceChange = MemberStoreChange.builder()
                .memberId(memberId)
                .changeNum(accountReq.getTransferNum())
                .coinsId(accountReq.getCoin().getCode())
                .coinsName(accountReq.getCoin().getName())
                .jaType(MemberJAType.TURN_OUT)
                .remark("转出" + targetMember.getUserAccount())
                .build();
        Integer sourceJAId = this.changeMemberStore(sourceStore, sourceChange);

        MemberStoreChange targetChange = MemberStoreChange.builder()
                .memberId(targetMember.getId())
                .changeNum(accountReq.getTransferNum())
                .coinsId(accountReq.getCoin().getCode())
                .coinsName(accountReq.getCoin().getName())
                .jaType(MemberJAType.TURN_IN)
                .jaId(sourceJAId)
                .remark(loginMember.getUserAccount() + "转入")
                .build();
        this.changeMemberStore(targetChange);

    }

    private boolean isDirectRelativeTransferAccounts(Member sourceMember, Member target) {
        Boolean isValid = configService.configValueIsValid(ConfigConstants.DIRECT_RELATIVE_TRANSFER_ACCOUNTS);
        return !isValid || memberService.isDirectRelative(sourceMember, target);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public MemberStore getMemberStore(Integer memberId, String coinsName) {
        MemberStore store = getOne(new QueryWrapper<MemberStore>().lambda()
                .eq(MemberStore::getMemberId, memberId)
                .eq(MemberStore::getCoinsName, coinsName));
        if (ObjectUtils.isEmpty(store)) {
            CoinsEnum coinsEnum = CoinsEnum.valueOf(coinsName);
            store = new MemberStore();
            store.setMemberId(memberId);
            store.setCoinsId(coinsEnum.getCode());
            store.setCoinsName(coinsEnum.getName());
            store.setNum(BigDecimal.ZERO);
            store.setFrostNum(BigDecimal.ZERO);
            store.setCreateTime(new Date());
            save(store);
        }
        return store;
    }

    @Override
    public Map<String, BigDecimal> getBalance2Map(Integer memberId) {
        return list(new QueryWrapper<MemberStore>().lambda().eq(MemberStore::getMemberId, memberId)).stream()
                .collect(Collectors.toMap(MemberStore::getCoinsName, MemberStore::getNum));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void payByMemberStore(Integer memberId, String coinsName, MemberJAType payType, BigDecimal payMoney, Integer orderId) {
        MemberStoreChange storeChange = MemberStoreChange.builder()
                .memberId(memberId)
                .changeNum(payMoney)
                .coinsName(coinsName)
                .orderId(orderId)
                .jaType(payType)
                .remark(payType.getMsg())
                .build();
        this.changeMemberStore(storeChange);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void getPointsByPayOrder(Integer memberId, BigDecimal giftPoints, String giftCoins, Integer orderId) {
        MemberStoreChange storeChange = MemberStoreChange.builder()
                .memberId(memberId)
                .changeNum(giftPoints)
                .coinsName(giftCoins)
                .orderId(orderId)
                .jaType(MemberJAType.GIFT_POINTS)
                .remark("购买礼包,赠送积分")
                .build();
        this.changeMemberStore(storeChange);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer changeMemberStore(MemberStoreChange storeChange) {
        MemberStore memberStore = getMemberStore(storeChange.getMemberId(), storeChange.getCoinsName());
        return changeMemberStore(memberStore, storeChange);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer changeMemberStore(MemberStore memberStore, MemberStoreChange storeChange) {
        Integer storeId = memberStore.getId();
        storeChange.getJaType().doOptStore(memberStore, storeChange.getChangeNum());
        BigDecimal storeNumNow = memberStore.getNum();
        BigDecimal frostNum = memberStore.getFrostNum();

        JournalAccount journalAccount = JournalAccount.builder()
                .storeId(memberStore.getId())
                .storeNum(memberStore.getNum())
                .coinsId(memberStore.getCoinsId())
                .coinsName(memberStore.getCoinsName())
                .storeFrostNum(frostNum)
                .memberId(memberStore.getMemberId())
                .jaNum(storeChange.getChangeNum())
                .jaType(storeChange.getJaType().getCode())
                .jaRemark(storeChange.getRemark())
                .orderId(storeChange.getOrderId())
                .orderDetailId(storeChange.getOrderDetailId())
                .jaId(storeChange.getJaId())
                .createTime(new Date())
                .build();

        memberStore = new MemberStore();
        memberStore.setId(storeId);
        memberStore.setNum(storeNumNow);
        memberStore.setFrostNum(frostNum);
        updateById(memberStore);
        journalAccountMapper.insert(journalAccount);
        return journalAccount.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean enoughOfBalance(Integer memberId, String coinsName, BigDecimal num) {
        MemberStore memberStore = getMemberStore(memberId, coinsName);
        return memberStore.getNum().compareTo(num) >= 0;
    }
}
